package com.peaksport.framework.extend.jpa;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.TemporalType;
import javax.persistence.metamodel.EntityType;

import org.hibernate.query.NativeQuery;
import org.hibernate.transform.Transformers;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.transaction.annotation.Transactional;

import com.peaksport.framework.base.entity.CoreBaseEntity;
import com.peaksport.framework.base.venum.BaseIntEnum;
import com.peaksport.framework.base.venum.BaseStringEnum;
import com.peaksport.framework.cache.SimpleMemoryCache;
import com.peaksport.framework.exception.PKDevException;
import com.peaksport.framework.map.NoCaseMap;
import com.peaksport.framework.metadata.view.EntityViewInfo;
import com.peaksport.framework.util.PKClassUtils;
import com.peaksport.framework.util.PKInvokeUtils;
import com.peaksport.framework.util.PKPublicUtils;
import com.peaksport.framework.uuid.PKEntityType;
import com.peaksport.framework.uuid.PKUuid;


@Transactional(readOnly = true)
public class PKJpaRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements PKJpaRepository<T,ID> {

	@SuppressWarnings("unused")
	private final Class<T> domainClass;
	private final EntityManager em;
	
	/** 本地内存缓存 */
	private static SimpleMemoryCache localCache = null;
	
	static {
		localCache = SimpleMemoryCache.getInstance();
	}

	public PKJpaRepositoryImpl(Class<T> domainClass, EntityManager em) {
		super(domainClass, em);
		this.domainClass = domainClass;
		this.em = em;
		
		getAllEntityType();
		
	}
	
	@SuppressWarnings("rawtypes")
	private void getAllEntityType() {
		Map<String,Class> mapClass = new NoCaseMap<>();
		Set<EntityType<?>> setEntityType = em.getEntityManagerFactory().getMetamodel().getEntities();
		Iterator<EntityType<?>> itEntityType = setEntityType.iterator();
		while (itEntityType.hasNext()) {
			EntityType<?> entityType = itEntityType.next();
			Class entityClass = entityType.getJavaType();
			if (PKClassUtils.isAssignableFrom(CoreBaseEntity.class, entityClass)) {
				Object entityObj = PKInvokeUtils.newInstance(entityClass);
				PKEntityType pkEntityType = ((CoreBaseEntity)entityObj).getEntityType();
				mapClass.put(pkEntityType.toString(), entityClass);
			}				
		}
		localCache.putCache(cache_key_forever_entityType, mapClass,true);
	}

	public EntityManager getEntityManager() {
		return em;
	}
	
	/**
	 * create a instance of query for execute
	 * @param sql
	 * @param resultClass
	 * @param isMapTransfer
	 * @return
	 */
	private NativeQuery<?> createNativeQuery(String sql, Class<?> resultClass, boolean isMapTransfer) {
		Query query = null;
		if (resultClass == null) query = em.createNativeQuery(sql);
		else if (resultClass != null && isMapTransfer) query = em.createNativeQuery(sql);
		else if (resultClass != null && !isMapTransfer) query = em.createNativeQuery(sql, resultClass);
		if (query instanceof NativeQuery) return (NativeQuery<?>)query;
		throw PKDevException.throwException("创建方言NativeQuery返回异常createNativeQuery,返回类型[%s]",query.getClass().getName());
	}
	
	@Override
	public Object queryOneValueForNative(String sql) {
		Object obj = queryOneRecordForNative(sql, null);
		if (obj == null) return obj;
		if (obj instanceof Object[]) throw new PKDevException(String.format("SQL语句Select字段只能1个,sql: [%s]", sql));
		return obj;
		
	}
	
	@Override
	public Object queryOneRecordForNative(String sql, Class<?> resultClass) {
		return queryOneRecordForNative(sql, resultClass,true);
	}
	
	/**
	 * 
	 * @param sql
	 * @param resultClass
	 * @param isMapTransfer
	 * @return
	 */
	@Override
	public Object queryOneRecordForNative(String sql, Class<?> resultClass, boolean isMapTransfer) {
		List<?> lstResult = queryRecordForNative(sql, resultClass,isMapTransfer);
		if (lstResult.isEmpty()) return null;
		else if (lstResult.size() == 1) return lstResult.get(0);
		else throw new PKDevException("执行queryOneRecordForNative超过1行数据");
	}
	@Override
	public List<?> queryRecordForNative(String sql, Class<?> resultClass) {
		return queryRecordForNative(sql, resultClass,true);
		
	}
	@SuppressWarnings({ "unchecked", "deprecation" })
	@Override
	public List<?> queryRecordForNative(String sql, Class<?> resultClass, boolean isMapTransfer) {
		NativeQuery<?> query = createNativeQuery(sql, resultClass, isMapTransfer);
		if (resultClass != null && isMapTransfer)
			query.unwrap(NativeQuery.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
			//query.unwrap(NativeQuery<HashMap>.class).setResultTransformer(Transformers.aliasToBean(HashMap.class));
		List<?> lstMapResult = query.getResultList();
		if (resultClass == null || !isMapTransfer) return lstMapResult;
		if (!PKClassUtils.isExtendBaseEntity(resultClass)) {
			throw PKDevException.throwException("返回类型[%s]非法,必须继承自[%s]", resultClass.getName(),CoreBaseEntity.class.getName());
		}
		List<CoreBaseEntity> lstResult = mapDataToEntity((List<Map<String, Object>>) lstMapResult, resultClass);
		return lstResult;
	}
	
	/**
	 * 将mapResult转换成实体对象
	 * 处理逻辑：
	 * 	1. 将lstMapReuslt每一行数据转换成实体entity对象,存放在lstEntityResult,一行的数据对应一个实体对象，不考虑主从实体的合并问题
	 *  2. 根据泛型List<CoreBaseEntity??>合并lstEntityResult成真实的实体清单lstResult
	 * @param lstMapResult
	 * @param resultClass
	 * @return
	 */
	@SuppressWarnings("unchecked")
	private List<CoreBaseEntity> mapDataToEntity(List<Map<String,Object>> lstMapResult, Class<?> resultClass)  {
		//根据lstMapResult按数据行转换成lstEntityResult
		List<CoreBaseEntity> lstEntityResult = new ArrayList<>(lstMapResult.size());
		//按数据库表字段名获取field缓存,为了兼容一些简单的SQL语句,没有使用正确的别名
		NoCaseMap<Field> mapAllFieldForTableField = PKClassUtils.getAllTableField(resultClass);
		//按实体属性获取field缓存
		NoCaseMap<Field> mapAllField = PKClassUtils.getAllField(resultClass);
		//合并field缓存
		mapAllField.putAll(mapAllFieldForTableField);
		//实体分录集合字段，一般根据在实体中含有List<CoreBaseEntry>集合进行判断
		Set<String> setGenericField = new HashSet<>();
		for (Map<String,Object> record: lstMapResult) {
			CoreBaseEntity objEntity = (CoreBaseEntity) PKInvokeUtils.newInstance(resultClass);
			for(Map.Entry<String,Object> entry: record.entrySet()) {
				Object value = entry.getValue();
				if (value == null) continue;
				String longFieldName = entry.getKey();
				String[] splitFieldName = longFieldName.split("\\.");
				//当前操作实体对象,注意在实体里实体对象的切换
				Object curObjEntity = objEntity;
				//当前实体的field
				NoCaseMap<Field> curMapAllField = mapAllField;
				for (int j = 0, jLen = splitFieldName.length; j < jLen; j++) {
					String fieldName = splitFieldName[j];
					Field field = curMapAllField.get(fieldName);					
					if (field == null) {
						throw PKDevException.throwException("解析实体[%s]属性/字段异常,找不到相应的属性/字段[%s]", resultClass.getName(),longFieldName);
					}
					fieldName = field.getName();
					Class<?> fieldClass = field.getType();
					if (PKClassUtils.isAssignableFrom(CoreBaseEntity.class, fieldClass)) { //普通的实体字段
						CoreBaseEntity coreBaseEntity = (CoreBaseEntity) PKInvokeUtils.newInstance(fieldClass);
						PKInvokeUtils.setFieldValue(curObjEntity, fieldName, coreBaseEntity);
						//当直接使用数据库xxID查询时,将ID值直接写入实体对象
						if (j == jLen - 1) coreBaseEntity.setId((String)value);
						curObjEntity = coreBaseEntity;
						curMapAllField = PKClassUtils.getAllField(curObjEntity.getClass());
						
					} else if (PKClassUtils.isAssignableFrom(List.class, fieldClass)) { //泛型
						fieldClass = PKClassUtils.getGenericClass(field);
						if (!PKClassUtils.isExtendBaseEntity(fieldClass)) 
							throw PKDevException.throwException("解析实体[%s]属性异常,集合分录泛型[%s]不合法,不是有效的CoreBaseEntry类型.",resultClass.getName(),fieldClass.getName());
						List<CoreBaseEntity> lstEntry = (List<CoreBaseEntity>) PKInvokeUtils.getFieldValue(curObjEntity, fieldName);
						if (lstEntry == null || lstEntry.isEmpty()) {
							if (lstEntry == null) {
								lstEntry = new ArrayList<>();
								PKInvokeUtils.setFieldValue(curObjEntity, fieldName, lstEntry);
							}
							curObjEntity = PKInvokeUtils.newInstance(fieldClass);
							lstEntry.add((CoreBaseEntity) curObjEntity);
							
							String genericField = "";
							for (int jj = 0;jj <= j; jj++) {
								genericField = genericField + splitFieldName[jj];
								if (jj <= j-1) genericField = genericField + ".";
							}
							
							setGenericField.add(genericField);
						} else {
							curObjEntity = lstEntry.get(0);
						}
						
						
						curMapAllField = PKClassUtils.getAllField(curObjEntity.getClass());
						
					} else if (PKClassUtils.isAssignableFrom(BaseIntEnum.class, fieldClass)) {
						value = BaseIntEnum.getEnum(fieldClass, ((BigDecimal)value).intValue());
						PKInvokeUtils.setFieldValue(curObjEntity, fieldName, value);
						
					} else if (PKClassUtils.isAssignableFrom(BaseIntEnum.class, fieldClass)) {
						value = BaseStringEnum.getEnum(fieldClass, (String)value);
						PKInvokeUtils.setFieldValue(curObjEntity, fieldName, value);
					} else {
						PKInvokeUtils.setFieldValue(curObjEntity, fieldName, value);
					}
				}
	
			}
			lstEntityResult.add(objEntity);
			
		}
		if (setGenericField.isEmpty()) { //没有集合分录泛型,直接返回结果
			return lstEntityResult;
		}
		
		List<String> lstGenericField = new ArrayList<>();
		for (String genericField : setGenericField) {
			lstGenericField.add(genericField);
		}
		//根据泛型List<CoreBaseEntity??>合并lstEntityResult成真实的实体清单lstResult
		List<CoreBaseEntity> lstResult = mergeData(lstEntityResult, lstGenericField);
		return lstResult;
	}
	
	
	/**
	 * 根据泛型List<CoreBaseEntity??>合并lstEntityResult成真实的实体清单lstResult
	 * 
	 * 合并逻辑:
	 * 1. 找到父节点
	 * 2. 找父节点的list
	 * 3. 插入父节点的list
	 * 
	 * @param lstEntityResult
	 * @param lstGenericField 泛型集合字段
	 * @return
	 */
	@SuppressWarnings("unchecked")
	private List<CoreBaseEntity> mergeData(List<CoreBaseEntity> lstEntityResult,List<String> lstGenericField) {
		Map<String,CoreBaseEntity> mapResult = new HashMap<>();
		Map<String, CoreBaseEntity> parentResult = new HashMap<>();
		for (int i = 0, len = lstEntityResult.size(); i < len; i++) {
			CoreBaseEntity curEntity = lstEntityResult.get(i);
			String id = curEntity.getId();
			CoreBaseEntity mergeEntity = mapResult.get(id);
			if (mergeEntity ==  null) {
				mapResult.put(id, curEntity);
				parentResult.put(id, curEntity);
			} else {
				CoreBaseEntity parentEntity = mergeEntity;
				for (int j = 0,jLen = lstGenericField.size(); j < jLen; j++) {
					String longGenericField = lstGenericField.get(j);
					String[] splitField = longGenericField.split("\\.");
					for (int k = 0, kLen =splitField.length; k < kLen; k++) {
						String fieldName = splitField[k];
						List<CoreBaseEntity> curLstEntryEntity = (List<CoreBaseEntity>) PKInvokeUtils.getFieldValue(curEntity, fieldName);
						CoreBaseEntity curEntryEntity = curLstEntryEntity.get(0);
						if (j == jLen - 1) {
							List<CoreBaseEntity> parentLstEntryEntity = (List<CoreBaseEntity>) PKInvokeUtils.getFieldValue(parentEntity, fieldName);
							parentLstEntryEntity.add(curEntryEntity);
						} else {
							String curEntryEntityId = curEntryEntity.getId();
							if (parentResult.get(curEntryEntityId) == null) {
								parentEntity = curEntryEntity;
								parentResult.put(curEntryEntityId, curEntryEntity);
							} else {
								parentEntity = parentResult.get(curEntryEntityId);
							}
						}
					
					}
					
				}

			}
			
		}
		List<CoreBaseEntity> lstNewResult = new ArrayList<>();
		for (Map.Entry<String, CoreBaseEntity> entry : mapResult.entrySet()) {
			lstNewResult.add(entry.getValue());
		}
		
		return lstNewResult;
		
	}
	

	
	@Override
	public boolean isExistsForNative(String sql) {
		List<?> list = queryRecordForNative(sql, null);
		return !PKPublicUtils.isEmpty(list);
		
	}
	
	@Override
	public boolean delete(String pk) {
		//TODO 增加日志,根据实体对象类型获取单据编码等。。
		
		String entityType = PKUuid.getECAObjectType(pk).toString();
		Class<?> entityClass = PKClassUtils.getEntityClass(entityType);
		String tableName = PKClassUtils.getTableName(entityClass);
		
		String sql = String.format("delete %s where FID='%s'", tableName,pk);
		executeSqlForNative(sql);
		return false;
	}
	
	@Override
	public int executeSqlForNative(String sql,String[] paramNames,Object[] paramValues,HashMap<String, TemporalType> dateTemporalType) {
		Query query = createNativeQuery(sql, null, false);
		if (paramNames == null) paramNames = new String[0];
		if (paramValues == null) paramValues = new Object[0];
		if (dateTemporalType == null) dateTemporalType = new HashMap<>(0);
		if (paramNames.length != paramValues.length) {
			throw PKDevException.throwException("执行executeSqlForNative(...)SQL脚本的参数名&参数值个数必须匹配.");
		}
		for (int i = 0, len = paramNames.length; i < len; i++) {
			String paramName = paramNames[i];
			Object paramValue = paramValues[i];
			if (paramValue instanceof Date || paramValue instanceof Calendar) {
				TemporalType temporalType = dateTemporalType.get(paramName);
				if (temporalType == null) temporalType = TemporalType.TIMESTAMP;
				if (paramValue instanceof Date)
					query.setParameter(paramName, (Date)paramValue, temporalType);
				else query.setParameter(paramName, (Calendar)paramValue, temporalType);
			} else {
				query.setParameter(paramName, paramValue);
			}
		}
		return query.executeUpdate();
	}
	@Override
	public int executeSqlForNative(String sql) {
		return executeSqlForNative(sql, null, null, null);
	} 
	
	/**
	 * 批量执行SQL
	 * @param em
	 * @param sqls
	 * @return
	 */
	@Override
	public int[] batchExecuteSqlForNative(String... sqls) {
		if (sqls == null) return null;
		int[] results = new int[sqls.length];
		for (int i = 0, len = sqls.length; i < len; i++) {
			results[i] = executeSqlForNative(sqls[i]);
		}
		return results;
	}
	
	/**
	 * 获取当前时间(数据库)
	 * @param em
	 * @return
	 */
	@Override
	public Date getCurrentTime() {
		String sql = "select sysdate from dual";
		return (Date)queryOneValueForNative(sql);
	}


	@SuppressWarnings("unchecked")
	@Override
	public <S extends T> S getValue(EntityViewInfo entityViewInfo, Class<T> entityClass) {
		String sql = entityViewInfo.toSQL(entityClass);
		return (S)queryOneRecordForNative(sql, entityClass);
	}


}
